package com.tagtraum.perf.gcviewer;

import com.google.common.base.Optional;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Rectangle;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.DefaultListModel;
import javax.swing.DefaultListSelectionModel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.ListModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

public class SectionGroup extends JPanel
  implements ListSelectionListener, SectionChangeListener
{
  private final DefaultListModel listModel;
  private HeadingsNotSelectable selectionModel;
  private final boolean isLarge;
  private JList list;

  public SectionGroup(SectionsModel model, boolean isLarge)
  {
    this.isLarge = isLarge;
    this.listModel = model.getListModel();
    buildView();
    SectionChangeBus.INSTANCE.subscribe(this);
    setupCursorChanges();
  }

  private void setupCursorChanges() {
    this.list.addMouseMotionListener(new MouseAdapter()
    {
      public void mouseMoved(MouseEvent event) {
        Rectangle cellBounds = SectionGroup.this.list.getCellBounds(0, SectionGroup.this.list.getModel().getSize() - 1);
        int cursorType = mouseInsideBounds(event, cellBounds) ? 12 : 0;
        SectionGroup.this.list.setCursor(new Cursor(cursorType));
      }

      private boolean mouseInsideBounds(MouseEvent event, Rectangle cellBounds) {
        return cellBounds.contains(event.getX(), event.getY());
      }
    });
  }

  private void buildView() {
    this.list = new JList(this.listModel);
    this.list.setCellRenderer(SectionCellRenderer.create(this.isLarge));
    this.selectionModel = new HeadingsNotSelectable();
    this.list.setSelectionModel(this.selectionModel);

    setLayout(new BorderLayout());
    add(this.list, "West");
    setBackground(Color.WHITE);

    this.list.addKeyListener(new KeyAdapter()
    {
      public void keyPressed(KeyEvent event) {
        if ((keyDown(event)) && (isNFromHeading(1))) {
          moveBy(2);
          event.consume();
        } else if ((keyUp(event)) && (isNFromHeading(-1))) {
          moveBy(-2);
          event.consume();
        }
      }

      private void moveBy(int amount) {
        int index = SectionGroup.this.list.getSelectedIndex() + amount;
        SectionGroup.this.select(index);
      }

      private boolean isNFromHeading(int i)
      {
        int index = SectionGroup.this.list.getSelectedIndex() + i;
        if (!SectionGroup.this.validIndex(index)) {
          return false;
        }
        return isHeading(index);
      }

      private boolean isHeading(int index) {
        Section section = (Section)SectionGroup.this.listModel.get(index);
        return section.isHeading();
      }

      private boolean keyDown(KeyEvent event) {
        return 40 == event.getKeyCode();
      }

      private boolean keyUp(KeyEvent event) {
        return 38 == event.getKeyCode();
      }
    });
  }

  private void select(int index)
  {
    if (validIndex(index)) {
      this.list.setSelectedIndex(index);
      this.list.ensureIndexIsVisible(index);
    }
  }

  private boolean validIndex(int index) {
    return (index >= 0) && (index < this.listModel.size());
  }

  public void valueChanged(ListSelectionEvent event)
  {
    int index = this.selectionModel.getMinSelectionIndex();
    Optional section = getSection(index);
    if (section.isPresent())
      SectionChangeBus.INSTANCE.switchSection(this, ((Section)section.get()).getName());
    System.out.println("dddd");
  }

  private Optional<Section> getSection(int index)
  {
    if (index == -1) {
      return Optional.absent();
    }
    return Optional.of((Section)this.listModel.get(index));
  }

  public void onChange(Object origin, String sectionName)
  {
    Optional index = getIndex(sectionName);
    if (index.isPresent())
      select(((Integer)index.get()).intValue());
    else
      this.selectionModel.clearSelection();
    
    System.out.println("hello world!");
  }

  private Optional<Integer> getIndex(String sectionName)
  {
    for (int i = 0; i < this.listModel.size(); i++) {
      Section section = (Section)this.listModel.get(i);
      if (sectionName.equals(section.getName())) {
        return Optional.of(Integer.valueOf(i));
      }
    }
    return Optional.absent();
  }

  private class HeadingsNotSelectable extends DefaultListSelectionModel
  {
    public HeadingsNotSelectable()
    {
      setSelectionMode(0);
      addListSelectionListener(SectionGroup.this);
      setValueIsAdjusting(true);
    }

    public void setSelectionInterval(int index, int onlyOneSelectableIndex)
    {
      Optional section = SectionGroup.this.getSection(index);
      if (!section.isPresent()) {
        return;
      }
      if (!((Section)section.get()).isHeading())
        super.setSelectionInterval(index, onlyOneSelectableIndex);
    }
  }
}